import json

from django.http import JsonResponse
from django.shortcuts import render

from apps.orders.models import OrderInfo,OrderGoods
from apps.user1.models import Address
# Create your views here.
from django.views import View
from apps.goods.models import SKU
from django_redis import get_redis_connection
from decimal import Decimal
from datetime import datetime
# 使用的的Django的事物方法
from django.db import transaction


class OrderView(View):
    def get(self,request):
        # 判断用户是否登录
        if not request.user.is_authenticated:
            # 用户未登录
            return JsonResponse({'code': 401, 'errmsg': '用户未登录'}, status=401)
        # 获取当前用户未删除的地址信息
        user = request.user
        addresses = Address.objects.filter(user_id = user.id,is_deleted = False)
        # 构造地址返回的结构形式
        address_list = []
        for addres in addresses:
            address_list.append({
                'id': addres.id,
                "province": addres.province.name,
                "city": addres.city.name,
                "district": addres.district.name,
                "place": addres.place,
                "mobile": addres.mobile,
                "receiver": addres.receiver,
            })
        # 获取redis中选中状态的sku商品进行返回
            # 3-1 连接reids
            conn = get_redis_connection("carts")
            # 3-2 获取set集合中的选中状态的skuid
            selected_sku = conn.smembers('selected_%d' % user.id)
            # 3-3 获取hash中的商品数量
            sku_count = conn.hgetall('sku_count_%d' % user.id)
            # 将sku_count中的bytes转化位int类型
            data_dict = {}
            for sku_id, count in sku_count.items():
                data_dict[int(sku_id)] = int(count)
            # 3-4 查询商品
            skus = SKU.objects.filter(id__in=selected_sku)
            # 3-5 遍历商品构造数据
            sku_list = []
            for sku in skus:
                sku_list.append({
                    'id': sku.id,
                    'name': sku.name,
                    "default_image_url": sku.default_image.url,
                    "count": data_dict[sku.id],
                    "price": sku.price,
                })
        context = {
            'addresses': address_list,
            'skus': sku_list,
            'freight': 10,
        }
        return JsonResponse({'code': 0,'context':context})


class OrdersSaveView(View):
    # 使用装饰器开启事物
    # @transaction.atomic
    def post(self,request):
        # 1、获取数据内容，地址id，支付方式
        data_json = request.body.decode()
        data_dict = json.loads(data_json)
        address_id = data_dict.get('address_id')
        pay_method = data_dict.get('pay_method')
        # 2、获取当前用户登录数据，在保存地址基本信息表时需要用到数据
        user = request.user
        # 3、验证数据
        if not all([address_id,pay_method]):
            return JsonResponse({'code':400,'errmsg':'参数不完整'},status=400)
        # 判断地址是否存在
        try:
            address = Address.objects.get(id = address_id)
        except:
            return JsonResponse({'code':400,'errmsg':'参数不完整'},status = 400)
        # 4、生成订单编号
        order_id = datetime.now().strftime('%Y%m%d%H%M%S') + '%09d' % user.id
        # 当要进行数据操作时，开启事务
        with transaction.atomic():
            # 事务使用 1、设置保存点，在出现错误时回滚到保存点
            save_point = transaction.savepoint()
            try:
                # 5、保存订单基本信息表
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,
                    total_amount=Decimal(0),  # 总价
                    freight=Decimal(10),  # 运费
                    pay_method=pay_method,
                    status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'] if pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY'] else OrderInfo.ORDER_STATUS_ENUM['UNSEND']
                )
                # 6、获取订单商品，商品从哪里来，从redis中获取选中状态的商品id，选中的商品时用户用户需要购买的下单商品
                # 连接redis
                conn = get_redis_connection('carts')
                sku_ids = conn.smembers('selected_%d' % user.id)
                # 获取商品的购买数据量，获取到的值是bytes类型
                sku_count = conn.hgetall('sku_count_%d' % user.id)
                # 将bytes类型转化为int
                data_dict = {}
                for sku_id, count in sku_count.items():
                    data_dict[int(sku_id)] = int(count)
                # 7、根据商品id查询商品表获取商品信息
                for sku_id in sku_ids:
                    while True:
                        # 查询sku数据
                        sku = SKU.objects.get(id = sku_id)
                        # 下单之后，商品的销量和库存量也要进行变化
                        old_sales = sku.sales
                        old_stock = sku.stock
                        # 购买数量不能大于库存
                        count = data_dict[sku.id]
                        if count > old_stock:
                            # 库存不足
                            transaction.savepoint_rollback(save_point)
                            return JsonResponse({'code': 400, 'errmsg': '库存不足'}, status=400)
                        # 修改sku商品库存和销量
                        # sku.stock = old_stock - count
                        # sku.sales = old_sales + count
                        # sku.save()
                        # 乐观锁使用，在更新数据前再次查询库存是否发生变化，如果发生变化，其他用户已经修改库存，则当前用户不能修改库存，重新只从查询原始库存操作，再次判断购买数量
                        res = SKU.objects.filter(id=sku.id, stock=old_stock).update(stock=old_stock - count,
                                                                                    sales=old_sales + count)
                        # 判断返回结果 res = 1 说明更行成功 res = 0 更新失败
                        if res == 0:
                            continue
                        # 修改spu商品总销量
                        sku.spu.sales += count
                        sku.spu.save()
                        # 累加商品价格和数量
                        order.total_count += count
                        order.total_amount += count * sku.price
                        # 8、保存订单商品表
                        OrderGoods.objects.create(
                            order=order,
                            sku=sku,
                            count=count,
                            price=sku.price
                        )
                        # 跳出循环
                        break
                # 当for循环结束后所有选中状态的商品已经处理完成，要累加的总价基础上在添加一个运费
                order.total_amount += order.freight
                order.save()
            except:
                # 捕获到异常，说明有代码出现问题需要回滚到保存点
                transaction.savepoint_rollback(save_point)
                # 没有捕获到异常 提交
            transaction.savepoint_commit(save_point)
        # 9、购物车中的选中商品已经下单，就需要将购物车选中状态商品进行清除
        conn.hdel('sku_count_%d' % user.id, *sku_ids)
        conn.srem('selected_%d' % user.id, *sku_ids)
        return JsonResponse({'code':0,'errmsg':'ok','order_id':order_id})